package com.qdlc.p2p.biz.web.interceptor;

import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;

import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.blogspot.radialmind.html.HTMLParser;
import com.blogspot.radialmind.html.HandlingException;
import com.blogspot.radialmind.xss.XSSFilter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.opensymphony.xwork2.ActionInvocation;
import com.qdlc.p2p.biz.util.NotifyRequestUrl;
import com.qdlc.p2p.common.util.StringUtil;
import com.qdlc.p2p.common.web.filter.SimplePropertyFilter;

public class ParamFilterInterceptor extends BaseInterceptor {

	/** 序列号 */
	private static final long serialVersionUID = -6325242223825713099L;
	/** 日志 */
	private static final Logger logger = Logger.getLogger(ParamFilterInterceptor.class);
	/** XSSFilter */
	private static final XSSFilter xssFilter = new XSSFilter();

	private static String denied_texts = "\\b(AND|OR)\\b.+?(>|<|=|\\bIN\\b|\\bLIKE\\b)|\\/\\*.+?\\*\\/|<\\s*SCRIPT\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|\\bON.+\\b\\s*=|\\bJAVASCRIPT\\s*:\\b|DECLARE|RENAME|JOIN|WHERE|LIKE|CAST|EVAL|<\\s*SCRIPT\\b|GRANT|UNICODE|CONFIRM|[0-9]AND[0-9]|[0-9]OR[0-9]|[0-9]+[0-9]TRIM|EXIST|BUTTON|[';<>\"&$%\\*]|</|[|]|\\\'|\\\"|<\\s>|()";
	private static String templet_denied_texts = "\\b(AND|OR)\\b.+?(>|<|=|\\bIN\\b|\\bLIKE\\b)|\\/\\*.+?\\*\\/|<\\s*SCRIPT\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|\\bON.+\\b\\s*=|\\bJAVASCRIPT\\s*:\\b|DECLARE|RENAME|JOIN|WHERE|LIKE|CAST|EVAL|<\\s*SCRIPT\\b|GRANT|UNICODE|CONFIRM|[0-9]AND[0-9]|[0-9]OR[0-9]|[0-9]+[0-9]TRIM|EXIST|BUTTON|[';\\*]|[|]|<\\s>|()";
	private static String url_texts = "SCRIPT|ALERT|BUTTON|TRIM|IFRAME|DECLARE|CONFIRM|HREF|LINK|INJECTED|WINDOW|LOCATION|EVAL|FUNCTION";

	private static List<String> paramsName = Lists.newArrayList();
	private static Set<String> greenUrl = Sets.newHashSet();

	static {
		paramsName.add("redirectURL");
		paramsName.add("htmlName");
		paramsName.add("picPath");
		paramsName.add("description");
		paramsName.add("requestParams");
		paramsName.add("attribute");
		greenUrl = NotifyRequestUrl.filtedNotifyUrl();
		greenUrl.add("/modules/system/sconfig/sconfigEdit");// 系统属性更新
		greenUrl.add("/modules/system/sconfig/sconfigAdd");// 系统属性增加
	}

	/**
	 * 初始化
	 */
	@Override
	public void init() {
		super.init();
	}

	/**
	 * 任何请求的参数 都要经过安全级别的过滤
	 */
	@Override
	public String intercept(ActionInvocation ai) throws Exception {
		HttpServletRequest request = ServletActionContext.getRequest();
		@SuppressWarnings("rawtypes")
		Enumeration names = request.getParameterNames();

		// 新浪回调 放行
		String url = request.getRequestURI();
		url = url.replaceAll(".action", "");
		if (url.contains("/tpp/tppAction/auditMemberCallBack") || url.contains("/tpp/tppAction/realNameCallBack")
				|| url.contains("/tpp/tppAction/authCallBack") || url.contains("/tpp/tppSinaAction/")
				|| greenUrl.contains(url)) {
			String result = ai.invoke();
			return result;
		}

		// 默认不包含有
		String hasDeniedText = null;
		// 默认不包含有
		boolean hasDeniedXss = false;
		String illegalParam = "";
		String checkText = denied_texts;
		while (names.hasMoreElements()) {
			String name = (String) names.nextElement();
			// 参数值为url地址时，请在paramsName中加入参数名称
			if (paramsName.contains(name)) {
				checkText = url_texts;
			}
			String[] values = request.getParameterValues(name);
			for (String value : values) {
				// 先对请求进行转码，然后转成大写
				illegalParam = value;
				String needCheckParams = value.replace("+", "%2B");// 前台传入的+特殊处理，解决+不能过滤的问题
				needCheckParams = UrlDecoder(needCheckParams).toUpperCase();

				if ("content".equals(StringUtil.isNull(name))) {// 富文本特殊处理
					hasDeniedXss = isAttack(needCheckParams);
					fliterXSSForContent(StringUtil.isNull(value));
				} else if ("templet".equals(StringUtil.isNull(name))) {// 消息模板特殊处理
					hasDeniedText = isDeniedText(needCheckParams, templet_denied_texts);
					hasDeniedXss = fliterXSS(StringUtil.isNull(value));
				} else {
					hasDeniedText = isDeniedText(needCheckParams, checkText);
					hasDeniedXss = fliterXSS(StringUtil.isNull(value));
				}
				// 文本校验
				if (StringUtil.isNotBlank(hasDeniedText)) {
					break;
				}
				// XSS校验
				if (hasDeniedXss) {
					break;
				}
			}
			// 文本校验
			if (StringUtil.isNotBlank(hasDeniedText)) {
				break;
			}
			// XSS校验
			if (hasDeniedXss) {
				break;
			}
			checkText = denied_texts;
		}
		// 文本过滤
		if (StringUtil.isNotBlank(hasDeniedText)) {
			logger.info("存在非法字符:  " + hasDeniedText + "");
			super.message("存在非法字符: " + hasDeniedText + "");
			responseJson("您输入的信息存在非法字符!", false);
			return null;
		}
		// xss过滤
		if (hasDeniedXss) {
			logger.info("存在非法字符: " + illegalParam);
			super.message("存在非法字符: " + illegalParam);
			responseJson("您输入的信息存在非法字符!", false);
			return null;
		}
		String result = ai.invoke();
		return result;
	}

	/**
	 * 校验是否为编辑器
	 * 
	 * @param name
	 * @param path
	 * @return
	 */
	public boolean isEdit(String name, String path) {
		if (name.equals("content")) {
			return true;
		}
		if ((name.equals("description") || name.equals("attribute"))
				&& (path.equals("/modules/user/score/addGoods.action")
						|| path.equals("/modules/user/score/editGoods.action"))) {
			return true;
		}
		return false;
	}

	/**
	 * 检查是否已经存在xss的字符
	 * 
	 * @param text 字符串
	 * @return 存在返回true
	 */
	public boolean fliterXSS(String text) {
		StringBuffer sb = new StringBuffer("<html>").append(text).append("</html>");
		StringReader reader = new StringReader(sb.toString());
		StringWriter writer = new StringWriter();

		StringReader noReader = new StringReader(sb.toString());
		StringWriter noWriter = new StringWriter();
		try {
			HTMLParser.process(reader, writer, xssFilter, true);
			HTMLParser.process(noReader, noWriter, null, true);
		} catch (Exception e) {
			return false;
		}

		String filterStr = writer.toString();
		String noFilterStr = noWriter.toString();

		return !filterStr.equals(noFilterStr);
	}

	public void responseJson(String msg, boolean result) throws Exception {
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("msg", msg);
		data.put("result", false);
		SimplePropertyFilter spf = new SimplePropertyFilter();
		SerializeWriter sw = new SerializeWriter();
		JSONSerializer serializer = new JSONSerializer(sw);
		serializer.getPropertyFilters().add(spf);
		serializer.write(data);
		HttpServletResponse response = ServletActionContext.getResponse();
		response.setContentType("application/json;charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.print(sw.toString());
		out.flush();
		out.close();
	}

	/**
	 * 检查是否已经存在xss的字符
	 * 
	 * @param text
	 * @return 不存在返回true
	 */
	public String fliterXSSForContent(String text) {
		StringBuffer sb = new StringBuffer("<html>").append(text).append("</html>");
		StringReader reader = new StringReader(sb.toString());
		StringWriter writer = new StringWriter();
		try {
			HTMLParser.process(reader, writer, xssFilter, true);
		} catch (HandlingException e) {
		}
		return writer.toString();
	}

	/**
	 * 返回true就是包含非法字符，返回false就是不包含非法字符
	 * 系统内容过滤规则
	 * 1、包含 『 and 1 特殊字符 』， 特殊字符指>,<,=, in , like 字符
	 * 2、『 /特殊字符/ 』，特殊字符指 *字符
	 * 3、『<特殊字符 script 』特殊字符指空字符
	 * 4、『 EXEC 』
	 * 5、『 UNION SELECT』
	 * 5、『 UPDATE SET』
	 * 5、『 INSERT INTO VALUES』
	 * 5、『 SELECT或DELETE FROM』
	 * 5、『CREATE或ALTER或DROP或TRUNCATE TABLE或DATABASE』
	 */
	public static boolean isAttack(String input) {
		String getfilter = "\\b(AND|OR)\\b.+?(>|<|=|\\bIN\\b|\\bLIKE\\b)|\\/\\*.+?\\*\\/|<\\s*SCRIPT\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|\\bON.+\\b\\s*=|\\bJAVASCRIPT\\s*:\\b";
		Pattern pat = Pattern.compile(getfilter);
		Matcher mat = pat.matcher(input);
		boolean rs = mat.find();
		return rs;
	}

	/**
	 * 字符串解码
	 * 
	 * @param sStr
	 * @return
	 */
	public static String UrlDecoder(String sStr) {
		String sReturnCode = sStr;
		try {
			sReturnCode = URLDecoder.decode(sStr, "utf-8");
		} catch (Exception e) {
		}
		return sReturnCode;
	}

	/**
	 * 通过数组 过滤
	 * 
	 * @param text
	 * @return
	 */
	public String isDeniedText(String text, String checkText) {
		StringBuffer strBuffer = new StringBuffer("");
		Pattern pat = Pattern.compile(checkText);
		Matcher mat = pat.matcher(text);
		while (mat.find()) {
			if (!StringUtil.isBlank(mat.group())) {
				strBuffer.append("  '" + mat.group() + "'   ");
			}
		}
		return strBuffer.toString();
	}

}
